
#include <BImage>
#include <BFile>
#include "ReadWrite_jpeg.h"

#include <stdio.h>
#include <jpeglib.h>

ReadWrite_jpeg::ReadWrite_jpeg()
{
	_author = "zym";
	_version = "1.0";
	_description = "This is BWE Plugin for reading *.jpg image file, writen width libjpeg, thank a lot !";
}
ReadWrite_jpeg::~ReadWrite_jpeg()
{

}

void ReadWrite_jpeg::setOptions(const BString& options)
{
	_options = options;
}
const BString& ReadWrite_jpeg::options() const
{
	return _options;
}

const BString& ReadWrite_jpeg::author() const
{
	return _author;
}
const BString& ReadWrite_jpeg::version() const
{
	return _version;
}
const BString& ReadWrite_jpeg::description() const
{
	return _description;
}
const BString& ReadWrite_jpeg::message() const
{
	return _message;
}

BObject* ReadWrite_jpeg::load(const BString& fileName)
{
	BString name = fileName.name();
	name.remove(name.find('.'), name.size());
	BImage* image = new BImage();
	image->setName(name);
	if (!load(fileName, image))
	{
		delete image;
		return 0;
	}
	image->setFileName(fileName);
	return image;
}
bool ReadWrite_jpeg::load(const BString& fileName, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
		return false;

	FILE* file = BFile::FOpen(fileName.cstr(), IO_Read);
	if (!file)
		return false;

	jpeg_decompress_struct cinfo;
	jpeg_error_mgr err;

	cinfo.err = jpeg_std_error(&err);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, file);

	jpeg_read_header(&cinfo, true);
	jpeg_start_decompress(&cinfo);

	int components = cinfo.out_color_components;
	Format format = Format_None;
	switch (cinfo.out_color_components)
	{
	case 4:
		format = Format_RGBA;
		break;
	case 3:
		format = Format_RGB;
		break;
	case 1:
		format = Format_Gray;
		break;
	default:
		fclose(file);
		return false;
	}

	int width = cinfo.image_width;
	int height = cinfo.image_height;
	int rowbytes = width * components;

	int size = width * height * components;
	BHolder<BByteArray> pixels = new BByteArray(rowbytes * height);
	BByte* data = pixels->data();

	BArray<BByte*> row_pointers(height);
	for (int row = 0; row < height; row++)
	{
		//int row = height - i - 1;
		row_pointers[row] = &data[rowbytes * row];
	}

	while (cinfo.output_scanline < cinfo.output_height)
	{
		jpeg_read_scanlines(&cinfo, &row_pointers[cinfo.output_scanline], cinfo.output_height - cinfo.output_scanline);
	}

	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	image->setFileName(fileName);
	image->setFormat(format);
	image->setWidth(width);
	image->setHeight(height);
	image->setPixels(pixels);

	if (image->format() == Format_Gray)
	{
		BByteArray* pixels = image->pixels();
		for (int i = 0; i < pixels->size(); i++)
			(*pixels)[i] = 255 - (*pixels)[i];
	}

	fclose(file);

	return true;
}
bool ReadWrite_jpeg::save(const BString& fileName, const BObject* object) const
{
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (image == 0)
		return false;

	int width = image->width();
	int height = image->height();
	Format format = image->format();
	BByte* data = (BByte*)image->pixels()->data();

	jpeg_compress_struct cinfo;
	jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);

	jpeg_create_compress(&cinfo);
	switch (format)
	{
	case Format_RGBA:
		cinfo.in_color_space = JCS_EXT_RGBA;
		cinfo.input_components = 4;
		break;
	case Format_RGB:
		cinfo.in_color_space = JCS_RGB;
		cinfo.input_components = 3;
		break;
	case Format_Gray:
		cinfo.in_color_space = JCS_GRAYSCALE;
		cinfo.input_components = 1;
		break;
	default:
		jpeg_destroy_compress(&cinfo);
		return false;
	}

	FILE* fp = BFile::FOpen(fileName.cstr(), IO_Write);
	if (fp == 0)
		return false;

	jpeg_set_defaults(&cinfo);
	jpeg_stdio_dest(&cinfo, fp);
	cinfo.image_width = width;
	cinfo.image_height = height;

	jpeg_start_compress(&cinfo, true);
	while (cinfo.next_scanline < cinfo.image_height)
	{
		int row = cinfo.next_scanline;
		BByte* row_pointer = data + row * width * cinfo.input_components;
		jpeg_write_scanlines(&cinfo, &row_pointer, 1);
	}
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	fclose(fp);

	return true;
}

BObject* ReadWrite_jpeg::read(const BBuffer& buffer)
{
	const BByte* indata = buffer.data();
	int insize = buffer.size();

	jpeg_decompress_struct cinfo;
	jpeg_error_mgr err;

	cinfo.err = jpeg_std_error(&err);
	jpeg_create_decompress(&cinfo);
	jpeg_mem_src(&cinfo, indata, insize);
	jpeg_read_header(&cinfo, true);
	jpeg_start_decompress(&cinfo);

	int components = cinfo.out_color_components;
	Format format = Format_None;
	switch (cinfo.out_color_components)
	{
	case 4:
		format = Format_RGBA;
		break;
	case 3:
		format = Format_RGB;
		break;
	case 1:
		format = Format_Gray;
		break;
	default:
		return 0;
	}

	int width = cinfo.image_width;
	int height = cinfo.image_height;
	int rowbytes = width * components;

	int size = width * height * components;
	BHolder<BByteArray> pixels = new BByteArray(rowbytes * height);
	BByte* data = pixels->data();

	BArray<BByte*> row_pointers(height);
	for (int row = 0; row < height; row++)
	{
		row_pointers[row] = &data[rowbytes * row];
	}

	while (cinfo.output_scanline < cinfo.output_height)
	{
		jpeg_read_scanlines(&cinfo, &row_pointers[cinfo.output_scanline], cinfo.output_height - cinfo.output_scanline);
	}

	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	BImage* image = new BImage();
	image->setFormat(format);
	image->setWidth(width);
	image->setHeight(height);
	image->setPixels(pixels);

	if (image->format() == Format_Gray)
	{
		BByteArray* pixels = image->pixels();
		for (int i = 0; i < pixels->size(); i++)
			(*pixels)[i] = 255 - (*pixels)[i];
	}

	return image;
}
bool ReadWrite_jpeg::read(const BBuffer& buffer, BObject* object)
{
	BImage* image = dynamic_cast<BImage*>(object);
	if (!image)
		return false;

	const BByte* indata = buffer.data();
	int insize = buffer.size();

	jpeg_decompress_struct cinfo;
	jpeg_error_mgr err;

	cinfo.err = jpeg_std_error(&err);
	jpeg_create_decompress(&cinfo);
	jpeg_mem_src(&cinfo, indata, insize);
	jpeg_read_header(&cinfo, true);
	jpeg_start_decompress(&cinfo);

	int components = cinfo.out_color_components;
	Format format = Format_None;
	switch (cinfo.out_color_components)
	{
	case 4:
		format = Format_RGBA;
		break;
	case 3:
		format = Format_RGB;
		break;
	case 1:
		format = Format_Gray;
		break;
	default:
		return false;
	}

	int width = cinfo.image_width;
	int height = cinfo.image_height;
	int rowbytes = width * components;

	int size = width * height * components;
	BHolder<BByteArray> pixels = new BByteArray(rowbytes * height);
	BByte* data = pixels->data();

	BArray<BByte*> row_pointers(height);
	for (int row = 0; row < height; row++)
	{
		row_pointers[row] = &data[rowbytes * row];
	}

	while (cinfo.output_scanline < cinfo.output_height)
	{
		jpeg_read_scanlines(&cinfo, &row_pointers[cinfo.output_scanline], cinfo.output_height - cinfo.output_scanline);
	}

	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	image->setFormat(format);
	image->setWidth(width);
	image->setHeight(height);
	image->setPixels(pixels);

	if (image->format() == Format_Gray)
	{
		BByteArray* pixels = image->pixels();
		for (int i = 0; i < pixels->size(); i++)
			(*pixels)[i] = 255 - (*pixels)[i];
	}

	return true;
}
bool ReadWrite_jpeg::write(BBuffer& buffer, const BObject* object) const
{
	const BImage* image = dynamic_cast<const BImage*>(object);
	if (image == 0)
		return false;

	int width = image->width();
	int height = image->height();
	Format format = image->format();
	BByte* data = (BByte*)image->pixels()->data();

	jpeg_compress_struct cinfo;
	jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);

	jpeg_create_compress(&cinfo);
	switch (format)
	{
	case Format_RGBA:
		cinfo.in_color_space = JCS_EXT_RGBA;
		cinfo.input_components = 4;
		break;
	case Format_RGB:
		cinfo.in_color_space = JCS_RGB;
		cinfo.input_components = 3;
		break;
	case Format_Gray:
		cinfo.in_color_space = JCS_GRAYSCALE;
		cinfo.input_components = 1;
		break;
	default:
		jpeg_destroy_compress(&cinfo);
		return false;
	}

	BByte* outbuffer = 0;
	unsigned long outsize = 0;

	jpeg_set_defaults(&cinfo);
	jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
	cinfo.image_width = width;
	cinfo.image_height = height;

	jpeg_start_compress(&cinfo, true);
	while (cinfo.next_scanline < cinfo.image_height)
	{
		int row = cinfo.next_scanline;
		BByte* row_pointer = data + row * width * cinfo.input_components;
		jpeg_write_scanlines(&cinfo, &row_pointer, 1);
	}
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	if (!outbuffer)
		return false;
	buffer.bytes().append(outbuffer, outsize);
	free(outbuffer);
	return true;
}
